/**********************************************************

@description:
*File Name:monkey.c
*Author: ydx
*mail: yudongxiang93@163.com
*Create Time: Sat 10 May 2025 07:20:15 AM CST
**********************************************************/
#include<stdio.h>
#include "./include/test.h"
#include<errno.h>
#include<math.h>
#include<sys/socket.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<netdb.h>
#include<pthread.h>
#include<ctype.h>
#include<sys/stat.h>

#define PRINT_STR(str) (printf("[%s - %d]"#str"=%s\n",__func__,__LINE__,str))
#define URL_LENGTH_DEFAULT 8192
#define LINE_MAX_LENGTH    8448

//init net ,port
//return socket
void error_die(const char *msg){
	perror(msg);
	exit(1);
}
int startup(unsigned short* port){
	//create socket
	int server_fd=socket(AF_INET,SOCK_STREAM,0);
	if(server_fd==-1){
		error_die("socket creation failed\n");
	}
	//set the port to be reusable
	int opt = 1;
	
	if(setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,(const char*)&opt,sizeof(opt)))
		error_die("setsockopt failed\n");
	//create sockaddr_in
	struct sockaddr_in server_addr;
	memset(&server_addr,0,sizeof(server_addr));
	server_addr.sin_family=AF_INET;
	server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	server_addr.sin_port=htons(*port); //host to net
	//bind server sock with the addr
	if(bind(server_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))<0){
		error_die("bind failed\n");
	}
	//set a port dynamic
	if(*port==0){
		socklen_t name_len=sizeof(server_addr);
		if(getsockname(server_fd,(struct sockaddr*)&server_addr,&name_len)<0){
			error_die("getsockname failed");
		}
		*port=server_addr.sin_port;
		printf("getsockname, get dynamic port:%d\n",*port);
	}
	//listen to the port and create listening queue
	if(listen(server_fd,5)==-1){
		error_die("listen failed\n");
	}
	return server_fd;
}

//server in io block
void* server_send_one_line(void *arg){
	int server_fd=*(int*)arg;
	printf("server_fd id:%d\n",server_fd);
	pthread_t tid=pthread_self();
	printf("pthread_self:%lu\n",(unsigned long)tid);
	const char *msg="hello from myserver";
	struct sockaddr_in client_addr;
	socklen_t client_len=sizeof(client_addr);

	int client_fd=accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
	if(client_fd==-1){
		error_die("accept failed:");
	}
	char ip_str[INET6_ADDRSTRLEN];
	char port_str[6];
	int err=getnameinfo((struct sockaddr*)&client_addr,client_len,ip_str,sizeof(ip_str),port_str,sizeof(port_str),NI_NUMERICHOST | NI_NUMERICSERV);
	if(err){
		fprintf(stderr,"getnameinfo error:%s\n",gai_strerror(err));
	}else{
		printf("client connected! client IP:%s, port:%s\n",ip_str,port_str);
	}
	//send data
	send(client_fd,msg,strlen(msg),0);
	close(client_fd);
	return NULL;
}

//read one line from sock
int get_line(int sock_fd,char *buf,int size){
	char ch=0;//'\0'
	int i=0;
	while(i<size-1 && ch !='\n'){
		int n=recv(sock_fd,&ch,1,0);
		if(n<0)
			error_die("recv failed");
		else if(n>0){
			if(ch=='\r'){
				n=recv(sock_fd,&ch,1,MSG_PEEK);
				if(n>0 && ch == '\n'){
					recv(sock_fd,&ch,1,0);
				}else{
					ch='\n';
				}
			}
			buf[i++]=ch;
		}else
			ch='\n';
	}
	buf[i]=0;
	return i;
}
void get_value_with_idx(char *dest,char *src,int start){
	int i=start;
	while(src && isspace(*(src+i)))
		i++;
	while(src && *(src+i)!=' '){
		dest[i]=src[i];
		i++;
	}
}
void get_value_with_char(char *dest,char *src,char ch){
	while(*src!=ch)
		src++;
	while(*src != ' '){
		*(dest++)=*(src++);
	}
}
void clear_sock(int client_fd){
	int num_char;
	char buf[LINE_MAX_LENGTH];
	while((num_char=get_line(client_fd,buf,sizeof(buf)))>0 && strcmp(buf,"\n")!=0){
		PRINT_STR(buf);
	}
}
const char* getHeadType(const char* file_name){
	const char *ret="text/html";
	const char *p=strrchr(file_name,'.');
	if(!p){
		PRINT_STR(ret);
		return ret;
	}
	p++;
	if(!strcmp(p,"css")) ret="text/css";
	else if(!strcmp(p,"jpg")) ret="image/jpeg";
	else if(!strcmp(p,"png")) ret="image/png";
	else if(!strcmp(p,"jpeg")) ret="image/jpeg";
	else if(!strcmp(p,"jsp")) ret="application/x-javascript";
	PRINT_STR(ret);
	return ret;
}
void unimplement(int client_fd,char *url){
	const char *msg="method not support";
	PRINT_STR(msg);
	//send data
	send(client_fd,msg,strlen(msg),0);
	close(client_fd);
}
void headers(int client_fd,const char *type){
	char buf[1024]="";
	strcpy(buf,"HTTP/1.1 200 OK\r\n");
	send(client_fd,buf,strlen(buf),0);

	strcpy(buf,"Content-Type:");
	strcat(buf,type);
	strcat(buf,"\r\n");
	send(client_fd,buf,strlen(buf),0);

	strcpy(buf,"Server: myhttpd/0.1\r\n");
	send(client_fd,buf,strlen(buf),0);
	
	strcpy(buf,"\r\n");
	send(client_fd,buf,strlen(buf),0);
}
void cat_file(int client_fd,FILE *f){
	char buf[4096];
	int count=0;
	while(1){
		int res=fread(buf,sizeof(char),sizeof(buf),f);
		if(res<=0){
			break;
		}
		count+=res;
		send(client_fd,buf,res,0);
	}
	printf("cat_file count=%d,send success\n",count);
}
void not_found(int client_fd){
#if 0
	//get sockaddr_in
	struct sockaddr_in client_addr;
	socklen_t client_len=sizeof(client_addr);
	if(getpeername(client_fd,(struct sockaddr*)&client_addr,&client_len)==-1){
		perror("getpeername failed");
		return;
	}
#endif
	clear_sock(client_fd);
	//send head
	char buf[1024];
	strcpy(buf,"HTTP/1.1 404 NOT FOUND\r\n");
	send(client_fd,buf,strlen(buf),0);

	strcpy(buf,"Content-Type:text/html\r\n");
	send(client_fd,buf,strlen(buf),0);

	strcpy(buf,"Server: myhttpd/0.1\r\n");
	send(client_fd,buf,strlen(buf),0);
	
	strcpy(buf,"\r\n");
	send(client_fd,buf,strlen(buf),0);
	//send data
	const char *msg="<!doctype html>  \
	<html>   \
		<title>Not FOUND</title> \
		<body> \
			<h2>the response is unavailable,</h2>  \
			<img src=\"./404.jpeg\"/>  \
		</body> \
	</html>";
	sprintf(buf,msg);

	ssize_t ret=send(client_fd,buf,strlen(buf),0);
	if(ret==-1)
		error_die("send msg failed");
	else{
		char sendRetStr[32]="";
		sprintf(sendRetStr,"send result:bytes:%4d",(int)ret);
		PRINT_STR(sendRetStr);
	}
}
void server_file(int client_fd,const char *path){
	PRINT_STR(path);
	clear_sock(client_fd);
	FILE *f=NULL;
	if(strcmp(getHeadType(path),"text/html")==0 || strcmp(getHeadType(path),"text/css")==0){
		f=fopen(path,"r");
	}else
		f=fopen(path,"rb");
	if(f==NULL){
		not_found(client_fd);
	}else{
		headers(client_fd,getHeadType(path));
		cat_file(client_fd,f);
		PRINT_STR("resource send success");
		fclose(f);
	}
}
void get_handler(int client_fd,char *url){
	//get sockaddr_in
	struct sockaddr_in client_addr;
	socklen_t client_len=sizeof(client_addr);
	if(getpeername(client_fd,(struct sockaddr*)&client_addr,&client_len)==-1){
		perror("getpeername failed");
		return;
	}
	//get path
	char path[LINE_MAX_LENGTH]="";
	sprintf(path,"htdocs%s",url);
	if(path[strlen(path)-1]=='/'){
		strcat(path,"index.html");
	}
	PRINT_STR(path);
	//check sock_fd
	int error=0;
	socklen_t error_len=sizeof(error);
	getsockopt(client_fd,SOL_SOCKET,SO_ERROR,&error,&error_len);
	if(error){
		perror("socket error");
		close(client_fd);
		return;
	}
	//check send buf
	int send_buf_size;
	getsockopt(client_fd,SOL_SOCKET,SO_SNDBUF,&send_buf_size,&error_len);
	printf("Send buffer size:%d\n",send_buf_size);

	struct stat statbuf;
	if(stat(path,&statbuf)==-1){
		not_found(client_fd);
	}else{
		if(statbuf.st_mode & S_IFMT == S_IFDIR){
			strcat(path,"/index.html");
		}
		if(S_ISREG(statbuf.st_mode)){
			server_file(client_fd,path);
		}else{
			not_found(client_fd);
		}
	}
#if 0
	//send data
	const char *msg="hello from myserver";
	PRINT_STR(msg);
	ssize_t ret=send(client_fd,msg,strlen(msg),0);
	if(ret==-1)
		error_die("send msg failed");
	else{
		char sendRetStr[32]="";
		sprintf(sendRetStr,"send result:bytes:%4d",(int)ret);
		PRINT_STR(sendRetStr);
	}
#endif
	close(client_fd);
}
void post_handler(int client_fd,char *url){

}
void* accept_request(void *arg){
	int server_fd=*(int*)arg;
	printf("server_fd id:%d\n",server_fd);
	pthread_t tid=pthread_self();
	printf("pthread_self:%lu\n",(unsigned long)tid);

	char buf[LINE_MAX_LENGTH];
	memset(buf,0,sizeof(buf));

	struct sockaddr_in client_addr;
	socklen_t client_len=sizeof(struct sockaddr_in);

	int client_fd=accept(server_fd,(struct sockaddr*)&client_addr,&client_len);
	if(client_fd==-1){
		error_die("accept failed:");
	}

	//read client ip and port
	char ip_str[INET6_ADDRSTRLEN];
	char port_str[6];
	int err=getnameinfo((struct sockaddr*)&client_addr,client_len,ip_str,sizeof(ip_str),port_str,sizeof(port_str),NI_NUMERICHOST | NI_NUMERICSERV);
	if(err){
		fprintf(stderr,"getnameinfo error:%s\n",gai_strerror(err));
	}else{
		printf("client connected! client IP:%s, port:%s\n",ip_str,port_str);
	}
#if 1
	//read one line
	int numbers = get_line(client_fd,buf,sizeof(buf));
	//GET / HTTP/1.1
	//printf("[%s - %d]buf=%s",__func__,__LINE__,buf);
	PRINT_STR(buf);
	//get method
	char method[8];
	memset(method,0,8*sizeof(char));
	int start=0;
	get_value_with_idx(method,buf,start);
	PRINT_STR(method);
	//get url
	char url[URL_LENGTH_DEFAULT];
	memset(url,0,255*sizeof(char));
	get_value_with_char(url,buf,'/');
	PRINT_STR(url);
	//dispatch request
	if(!strcasecmp(method,"GET")){
		get_handler(client_fd,url);
	}else if(!strcasecmp(method,"POST")){
		post_handler(client_fd,url);
	}else{
		unimplement(client_fd,url);
	}
#endif
	close(client_fd);
	return NULL;
}



//server in nio block/thread
void server_with_thread(void *server_fd){
	pthread_t tid;
	int res;
	void *tret;
	res=pthread_create(&tid,NULL,accept_request,server_fd);
	if(res){
		error_die("pthread_create failed");
	}
	printf("process id<%d> thread id<%lu>\n",getpid(),pthread_self());
#if 1
	res=pthread_join(tid,&tret);
	if(res){
		error_die("pthread_join error");
	}
#endif
}

//server in nio block/thread
void server_with_thread_test(void *server_fd){
	pthread_t tid;
	int res;
	void *tret;
	res=pthread_create(&tid,NULL,server_send_one_line,server_fd);
	if(res){
		error_die("pthread_create failed");
	}
	printf("process id<%d> thread id<%lu>\n",getpid(),pthread_self());
#if 1
	res=pthread_join(tid,&tret);
	if(res){
		error_die("pthread_join error");
	}
#endif
}

int main(int argc,char** argv){
	int ret=0;
	unsigned short port=0;
	int server_fd;
	welcome();
	if(argc>=2){
		port=(unsigned short)atoi(argv[1]);
	}
	server_fd= startup(&port);
	if(server_fd<0){
		printf("startup failed! error code:%d\n",server_fd);
		exit(1);
	}
	printf("hpptd server started, listening on port %d ...\n",port);

	while(1){
		//server_with_thread_test(&server_fd);
		server_with_thread(&server_fd);
	}
close:
	close(server_fd);
	return ret;
}
